/////////////////////////////////////////////////////////////////////////////
// Purpose:     Remote connections through sockets management
// Author:      Jesus Gonzalez
// Modified by:
// Copyright:   (c) 2003-2013 Jesus Gonzalez <jgonzalez@gdr-sistemas.com>
// License:     BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _SMARTLIB_SOCKET_H_
#define _SMARTLIB_SOCKET_H_

/**
 * \file
 * Remote connections through sockets management.
 */

#include <SmartLib/SmartPtr.h>
#include <SmartLib/ExtendedTypes.h>
#include <SmartLib/ByteArray.h>
#include <string>

///@defgroup networking Networking
///@{

/**
 * The Socket abstract class represents an endpoint of an interprocess communication
 * flow.
 *
 * @see ClientSocket ServerSocket
 */
class Socket : public SmartObject
{
public:
	DECLARE_SMARTPTR(Socket);

	/**
	 * Destructor.
	 */
	virtual ~Socket();

	/**
	 * Closes the socket.
	 */
	void Close();

protected:
	///@cond INTERNAL
    /**
     * Default constructor.
     */
	Socket();

	/**
	 * Creates a Socket from an open descriptor
	 * @note Internal Usage
	 */
	Socket( int fd );

	int m_fd;
	///@endcond
};

/**
 * The RemoteSocket abstract class represents the endpoint of an interprocess communication
 * flow with a remote machine.
 *
 * @see ClientSocket ServerSocket
 */
class RemoteSocket : public Socket
{
public:
	DECLARE_SMARTPTR(RemoteSocket);

	/**
	 * Waits for incoming data to be ready.
	 *
	 * @param[in] timeout Timeout in milliseconds, 0 to return immediately, -1 to wait indefinitely.
	 * @return \p True if data can be received, \p false otherwise.
	 */
	bool WaitForReception( int timeout );

	/**
	 * Receives incoming data from the socket.
	 *
	 * @param[out] buffer Buffer to store received data.
     * @param[in] size Maximum number of bytes to be read.
	 */
	void Receive( ByteArray &buffer, uint size = UINT_MAX );

	/**
	 * Receives incoming data from the socket.
	 *
     * @param[in] size Maximum number of bytes to be received.
	 * @return Received data. If the returned ByteArray length is 0, the socket has been closed.
	 */
	ByteArray Receive( uint size = UINT_MAX );

	/**
	 * Send data through the socket.
	 *
	 * @param[in] message Buffer storing the data to be sent.
	 */
	void Send( const ByteArray &message );

	/**
	 * Gets the address of the remote machine to which the socket is connected.
     *
     * @return Remote address.
	 */
	std::string GetAddress();

    /**
     * Gets the port on the remote machine to which the socket is connected.
     *
     * @return Remote port.
     */
	uint16 GetPort();

    /**
     * Gets the address and port on the remote machine to which the socket is connected.
     *
     * @param[out] address Buffer to store the remote address.
     * @param[out] port Variable to store the remote port.
     */
	void GetInfo( std::string &address, uint16 &port );

protected:
    ///@cond INTERNAL
	/**
	 * Default constructor.
	 */
	RemoteSocket() {}

    /**
     * Creates a RemoteSocket from an open descriptor
     * @note Internal Usage
     */
	RemoteSocket(int fd) : Socket(fd) {}
    ///@endcond

	friend class ServerSocket;
};

/**
 * The ClientSocket class is used to get access to network services offered
 * by remote machines.
 *
 * A ClientSocket can establish a communication flow with a remote
 * machine by which data can be sent or received.
 */
class ClientSocket : public RemoteSocket
{
public:
	DECLARE_SMARTPTR_NEWCTOR(ClientSocket);

    ClientSocket() {};

    /**
     * Connects to a remote host.
     *
     * @param[in] hostname Address of the host.
     * @param[in] port Port number or service name of the host's port to connect to.
     * @param[in] timeout Timeout in milliseconds, 0 to return immediately, -1 to use default timeout.
     */
	void Connect( const std::string &hostname, const std::string &port, int timeout = -1 );

    /**
     * Connects to a remote host.
     *
     * @param[in] hostname Address of the host.
     * @param[in] port Port number or service name of the host's port to connect to.
     * @param[in] timeout Timeout in milliseconds, 0 to return immediately, -1 to use default timeout.
     */
	void Connect( const std::string &hostname, uint16 port, int timeout );

    /**
     * Connects to a remote host.
     *
     * @param[in] hostname Address and port of the host in 'host:port' format.
     * @param[in] timeout Timeout in milliseconds, 0 to return immediately, -1 to use default timeout.
     */
    void Connect( const std::string &hostname, int timeout = -1 );

    /**
     * Checks whether the socket is connected to a remote host.
     *
     * @return True if the socket is connected, false otherwise.
     */
    bool isConnected() const;

private:
    bool Connect(struct addrinfo *ai, struct timeval* ts_timeout);
};

/**
 * The ServerSocket class is used to offer network services to remote machines.
 *
 * A ServerSocket can wait for remote clients to connect to the offered service,
 * then will establish a communication flow with these remote machines by which
 * data can be sent or received.
 */
class ServerSocket : public Socket
{
public:
	DECLARE_SMARTPTR_NEWCTOR(ServerSocket);

    ServerSocket() {};

    /**
     * Prepares the socket to receive incoming connections.
     *
     * After calling this method to prepare the socket to receive remote
     * connections, Accept() should be called to accept each individual
     * connection.
     *
     * @param[in] port Port that will accept connections.
     * @param[in] queue_size Number of connections that can be queued.
     */
	void Listen( uint16 port, int queue_size );

    /**
     * Waits for incoming connection to be ready.
     *
     * @param[in] timeout Timeout in milliseconds, 0 to return immediately, -1 to wait indefinitely.
     * @return True if a connection is available, false otherwise.
     */
	bool Wait(int timeout);

	/**
	 * Accepts a remote connection.
	 *
	 * @return Socket that can be used to communicate with the remote machine.
	 */
	RemoteSocket::Ptr Accept();

    /**
     * Gets the address of the local machine.
     *
     * @return Local address.
     */
	std::string GetAddress();

    /**
     * Gets the port on the local machine on which the connections are received.
     *
     * @return Local port.
     */
	uint16 GetPort();

    /**
     * Gets the address and port on the local machine on which the connections are received.
     *
     * @param[out] address Buffer to store the local address.
     * @param[out] port Variable to store the local port.
     */
	void GetInfo( std::string &address, uint16 &port );
};

///@}

#endif // _SMARTLIB_SOCKET_H_
